//
// Created by root on 6/29/17.
//

#include <stdio.h>
#include <stdlib.h>
#include "cell.h"
#include "constants.h"

int is_pointer(Cell* p) {
    return (p->_flag) & REF_POINTER;
}

int is_variable(Cell* p) {
    int is_pointer = (p->_flag) & REF_POINTER;
    int is_immutable = (p->_flag) & REF_IMMUTABLE;
    return is_pointer && !is_immutable;
}

int is_constant(Cell* p) {
    int is_pointer = (p->_flag) & REF_POINTER;
    int is_immutable = (p->_flag) & REF_IMMUTABLE;
    return is_pointer && is_immutable;
}

void set_pointer(Cell* p) {
    p->_flag |= REF_POINTER;
}

// 是否immutable
int is_immutable(Cell* p) {
    return (p->_flag) & REF_IMMUTABLE;
}

// 设为不可改变
void set_immutable(Cell* p) {
    p->_flag |= REF_IMMUTABLE;
}

// 是否mark
int is_mark(Cell* p) {
    return (p->_flag) & REF_MARK;
}

// 设置mark
void set_mark(Cell* p) {
    (p->_flag) |= REF_MARK;
}

// 清除mark
void clear_mark(Cell* p) {
    (p->_flag) &= ~REF_MARK;
}


/*
 * car/cdr/cons函数
 * */
Cell* car(Cell* p) {
    return p->_pair._car;
}

Cell* cdr(Cell* p) {
    return p->_pair._cdr;
}

Cell* cons(Scheme *sc, Cell* a, Cell* b) {
    return make_cell(sc, a, b);
}

Cell* immutable_cons(Scheme *sc, Cell* a, Cell* b) {
    Cell* x = make_cell(sc, a, b);
    set_immutable(x);
    return (x);
}


/* allocate new cell segment */
int alloc_cellseg(Scheme *sc, int n) {
    int k;
    Cell* p;

    for (k = 0; k < n; k++) {
        if (sc->lastCellSeg >= CELL_NSEGMENT - 1)
            return k;
        sc->cellSeg[sc->lastCellSeg + 1] = (Cell*) malloc((CELL_SEGSIZE + 1) * sizeof(Cell));
        long i = ++sc->lastCellSeg;
        Cell* p_new = sc->cellSeg[i];
        while (i > 0 && sc->cellSeg[i - 1] > sc->cellSeg[i]) { //插入排序，sc->cell_seg中的元素安装指针值的从小大排序
            sc->cellSeg[i] = sc->cellSeg[i - 1];
            sc->cellSeg[--i] = p_new;
        }
        sc->freeCellCount += CELL_SEGSIZE; //增加空闲单元的计数
        //所有的空闲cell构成一个链表，并且依照位置值从小到大排列
        Cell* last = p_new + CELL_SEGSIZE - 1;
        for (p = p_new; p <= last; p++) {
            (p)->_flag = 0;
            p->_pair._cdr = p + 1;
            p->_pair._car = G_NIL;
        }
        /* insert new cells in address order on free list */
        if (sc->freeCell == G_NIL || last < sc->freeCell) {
            last->_pair._cdr = sc->freeCell;
            sc->freeCell = p_new;
        } else {
            Cell* p = sc->freeCell;
            while ((p)->_pair._cdr != G_NIL && p_new > (p)->_pair._cdr)
                p = (p)->_pair._cdr;
            last->_pair._cdr = p->_pair._cdr;
            p->_pair._cdr = p_new;
        }
    }
    return n;
}

/* get new cell.  parameter a, b is marked by gc. */
Cell* create_cell_helper(Scheme *sc, Cell* a, Cell* b) {		//分配一个cell
    if (sc->freeCell == G_NIL) {
        scheme_gc(sc, a, b);
        if (sc->freeCell == G_NIL) {
            if (!alloc_cellseg(sc, 1) && sc->freeCell == G_NIL) {
                printf("No Memory ! ! !\n");
                while (1)
                    getchar();
            }
        }
    }
    Cell* p = sc->freeCell;
    sc->freeCell = (p)->_pair._cdr;
    --sc->freeCellCount;
    return p;
}

Cell *alloc_cell() {
    Cell *result = (Cell *) malloc(sizeof(Cell));
    result->_type = UNKNOWN_TYPE;
    result->_name = UNKNOWN_NAME;
    return result;
}


Cell* make_cell(Scheme *sc, Cell* a, Cell* b) {
    Cell* p = create_cell_helper(sc, a, b);
    /* For right now, include "a" and "b" in "cell" so that gc doesn't think they are garbage. */
    /* Tentatively record it as a pair so gc understands it. */
    // (p)->_flag = T_PAIR;
    (p)->_type = PAIR_TYPE;
    (p)->_pair._car = a;
    (p)->_pair._cdr = b;
    return p;
}
